/******************************************************************************
 ******************************************************************************
  File Name     : TransferRtpDataToUdpData.c
  Version       : Initial Draft
  Author        : Su<sugao_cn@163.com>
  Created       : 2013/12/26
  Last Modified :
  Description   :     Rtp data to udp data
  Function List :
  History       :
  1.Date        : 2013/12/26
    Author      :Su<sugao_cn@163.com>
    Modification: Created file

******************************************************************************/

#define MSDRM_C

/*----------------------------------------------*
 * include files            	 ������ͷ�ļ�   *
 *----------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libmscommon/mscommon.h>
#include <libmscommon/msthread.h>
#include <libmscommon/mstime.h>
#include <libmsprotocol/msprotocol.h>
#include <libmslog/mslog.h>
#include "msaes.h"  
#include "msdrm.h"  
//#include "internal.h"
/*----------------------------------------------*
 * macros �궨��                                *
 *----------------------------------------------*/
#define FLAG "msdrm"

/*
|0	1	2	3	4	5	6	7	|0	1	2	3	4	5	6	7	|0	1	2	3	4	5	6	7	|0	1	2	3	4	5	6	7	|
|<--						synchead															   -->|<---		hopt				   -->|
|<--						datalen							   -->|<--						payloadlen						   -->|
|<--															res															   -->|
|<--															res															   -->|
synchead(3 byte):must be "avs"
hopt(1 byte):
	bit[0]:Identify whether data is encrypted;
	bit[1:7]:res;
datalen(2 byte):all data lengths, including load data and fill data;
payloadlen(2 byte):load data lengths;
*/
#define msdrm_synchead_offset 		0
#define msdrm_synchead  			"avs"
#define msdrm_synchead_len 		ms_buflen(msdrm_synchead)
#define msdrm_hopt_offset 			(msdrm_synchead_offset+msdrm_synchead_len)
#define msdrm_hopt_len			1
#define msdrm_datalen_offset 		(msdrm_hopt_offset+msdrm_hopt_len)
#define msdrm_datalen_len 			2
#define msdrm_payloadlen_offset 	(msdrm_datalen_offset+msdrm_datalen_len)
#define msdrm_payloadlen_len		2
#define msdrm_head_len 			16
#define msdrm_data_offset			msdrm_head_len

#define msdrm_len_data(len,buf)		len=ms_host_2b(  &((buf)[msdrm_datalen_offset])   );
#define msdrm_len_payload(len,buf)	len=ms_host_2b(  &((buf)[msdrm_payloadlen_offset])   );

#define msdrm_time_decenc			ms_usminutes(30)
typedef struct{
	ms_u08 	opt;
	ms_u32	keylen;
	ms_s08  	key[32];
	ms_u64	timestamp_keyupdate;
}MSDRMContext;


static DODrm 	*first_drm=ms_null;
/*----------------------------------------------*
 * routines' implementations   ������ʵ��       *
 *----------------------------------------------*/
static ms_bool msdrm_innerapi_ishead(ms_pbyte  ms_in pbuf, ms_u32 ms_in len)
{
	return ( ms_noless(len, msdrm_synchead_len)
			&&(ms_strncmps_eq(pbuf,msdrm_synchead))
		) ? ms_true : ms_false;	
}
static void * msdrm_api_init(void)
{
	void * pdrmval=(void * )ms_malloc(sizeof(MSDRMContext),"MSDRMContext");
	memset(pdrmval, 0, sizeof(MSDRMContext));
	return pdrmval;
}
static void msdrm_api_setopt(void *pdrmval,ms_u08 opt)
{
	MSDRMContext * pmsdrm_ctt=(MSDRMContext * )pdrmval;
	pmsdrm_ctt->opt=opt;
}
static void msdrm_api_setkey32(ms_u08 * inkey,ms_u08 * outkey)
{
	outkey[0]=inkey[11];
	outkey[1]=inkey[13];
	outkey[2]=inkey[15];
	outkey[3]=inkey[5];
	outkey[4]=inkey[18];
	outkey[5]=inkey[23];
	outkey[6]=inkey[4];
	outkey[7]=inkey[14];
	outkey[8]=inkey[3];
	outkey[9]=inkey[1];
	outkey[10]=inkey[20];
	outkey[11]=inkey[21];
	outkey[12]=inkey[2];
	outkey[13]=inkey[22];
	outkey[14]=inkey[10];
	outkey[15]=inkey[0];
	outkey[16]=inkey[16];
	outkey[17]=inkey[7];
	outkey[18]=inkey[19];
	outkey[19]=inkey[17];
	outkey[20]=inkey[6];
	outkey[21]=inkey[9];
	outkey[22]=inkey[12];
	outkey[23]=inkey[8];
	outkey[24]=inkey[30];
	outkey[25]=inkey[27];
	outkey[26]=inkey[25];
	outkey[27]=inkey[31];
	outkey[28]=inkey[28];
	outkey[29]=inkey[26];
	outkey[30]=inkey[24];
	outkey[31]=inkey[29];
}
static void msdrm_api_setkey24(ms_u08 * inkey,ms_u08 * outkey)
{
	outkey[0]=inkey[11];
	outkey[1]=inkey[13];
	outkey[2]=inkey[15];
	outkey[3]=inkey[5];
	outkey[4]=inkey[18];
	outkey[5]=inkey[23];
	outkey[6]=inkey[4];
	outkey[7]=inkey[14];
	outkey[8]=inkey[3];
	outkey[9]=inkey[1];
	outkey[10]=inkey[20];
	outkey[11]=inkey[21];
	outkey[12]=inkey[2];
	outkey[13]=inkey[22];
	outkey[14]=inkey[10];
	outkey[15]=inkey[0];
	outkey[16]=inkey[16];
	outkey[17]=inkey[7];
	outkey[18]=inkey[19];
	outkey[19]=inkey[17];
	outkey[20]=inkey[6];
	outkey[21]=inkey[9];
	outkey[22]=inkey[12];
	outkey[23]=inkey[8];
}
static void msdrm_api_setkey16(ms_u08 * inkey,ms_u08 * outkey)
{
	outkey[0]=inkey[10];
	outkey[1]=inkey[4];
	outkey[2]=inkey[15];
	outkey[3]=inkey[11];
	outkey[4]=inkey[2];
	outkey[5]=inkey[12];
	outkey[6]=inkey[9];
	outkey[7]=inkey[6];
	outkey[8]=inkey[1];
	outkey[9]=inkey[5];
	outkey[10]=inkey[13];
	outkey[11]=inkey[7];
	outkey[12]=inkey[0];
	outkey[13]=inkey[14];
	outkey[14]=inkey[8];
	outkey[15]=inkey[3];
}
static void msdrm_api_setkey(void *pdrmval,ms_u08 * key,ms_s32 len)
{
	//can be 16,24,32
	ms_debug("The len of key is %d",len);
	MSDRMContext * pdrm_ctt=(MSDRMContext * )pdrmval;
	if(ms_noless(len,32)){
		pdrm_ctt->keylen=32;
		msdrm_api_setkey32(key, pdrm_ctt->key);
	}else if(ms_noless(len,24)){
		pdrm_ctt->keylen=24;
		msdrm_api_setkey24(key, pdrm_ctt->key);
	}else if(ms_noless(len,16)){
		pdrm_ctt->keylen=16;
		msdrm_api_setkey16(key, pdrm_ctt->key);
	}else{
		ms_waring("The len of key(%d) is too short,use default key",len);
		pdrm_ctt->keylen=16;
		ms_s08 defaultkey[16]={
			0x65,0x6e, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 
			0x6f, 0x6e, 0x31, 0x31,  0x31, 0x31, 0x31, 0x31
		};
		msdrm_api_setkey16(defaultkey, pdrm_ctt->key);
	}
}

static ms_bool msdrm_innerapi_endec(EN_CRYPT_TYPE encypt,MSDRMContext * pmsdrm_ctt,ms_u08 *inbuf,ms_u08 *pdrmbuf,ms_s32 datalen )
{
	switch(pmsdrm_ctt->opt&msdrm_emopt_mask_entype){
		case msdrm_em_aes_ecb:
			if(pmsdrm_ctt->opt&msdrm_emopt_mask_dataenc){
				if( ms_false==msaes_api_done(encypt, CRYPT_MODE_ECB, inbuf, pdrmbuf, datalen,  pmsdrm_ctt->key,  pmsdrm_ctt->keylen) ){
					ms_error("msaes_api_done(ecb)  failed , noencrypt it");
					pmsdrm_ctt->opt&=(~msdrm_emopt_mask_dataenc);
					ms_memcpy(pdrmbuf, inbuf,datalen);	
				}
			}else{
				ms_memcpy(pdrmbuf, inbuf,datalen);
			}
			break;
		case msdrm_em_aes_cbc:
			if(pmsdrm_ctt->opt&msdrm_emopt_mask_dataenc){
				if( ms_false==msaes_api_done(encypt, CRYPT_MODE_CBC, inbuf, pdrmbuf, datalen,  pmsdrm_ctt->key,  pmsdrm_ctt->keylen) ){
					ms_error("msaes_api_done(cbc)  failed , noencrypt it");
					pmsdrm_ctt->opt&=(~msdrm_emopt_mask_dataenc);
					ms_memcpy(pdrmbuf, inbuf,datalen);	
				}
			}else{
				ms_memcpy(pdrmbuf, inbuf,datalen);
			}
			break;
		default:
			break;
	}
}

static ms_s32 msdrm_api_enc(void *pdrmval,ms_u08 *inbuf,ms_u08 *outbuf,ms_s32 *plen )
{
	MSDRMContext * pmsdrm_ctt=(MSDRMContext * )pdrmval;
	//malloc drmbuf
	ms_u08 *pdrmbuf=ms_null;
	ms_malloc_ret(pdrmbuf, ( (*plen)*2), "drmenc_buf", -1);

	/*data must be x*16,so I will res the left*/
	ms_u32 payloadlen=(*plen);
	ms_u32 len_fill=(0==((*plen)%msdrm_head_len)) ? 0 : (16-((*plen)%msdrm_head_len));	
	ms_u32 datalen=payloadlen+  len_fill;	

	if((mstime_api_us()-pmsdrm_ctt->timestamp_keyupdate)>msdrm_time_decenc){
		pmsdrm_ctt->opt|=msdrm_emopt_mask_dataenc;		
	}else{
		pmsdrm_ctt->opt&=(~msdrm_emopt_mask_dataenc);	
	}

	//ms_debug("pmsdrm_ctt->opt:%d", pmsdrm_ctt->opt);
	msdrm_innerapi_endec(CRYPT_TYPE_ENCRYPT, pmsdrm_ctt, inbuf, pdrmbuf, datalen);
/*Drm header*/	
	//sysnhead-3
	ms_memcpy(&outbuf[msdrm_synchead_offset],msdrm_synchead,msdrm_synchead_len);
	//opt-1
	outbuf[msdrm_hopt_offset]=pmsdrm_ctt->opt;
	//datalen-2
	ms_network_2b(&outbuf[msdrm_datalen_offset], datalen);
	//payloadlen-2
	ms_network_2b(&outbuf[msdrm_payloadlen_offset], payloadlen);
/*Data*/	
	ms_memcpy(&outbuf[msdrm_data_offset],pdrmbuf,datalen);
	ms_demalloc(pdrmbuf);

	(*plen)=(*plen)+msdrm_head_len+len_fill;
	//ms_debug("--------------------------%d",(*plen));
	return 0;
}
static ms_s32 msdrm_api_dec(void *pdrmval,ms_u08 *inbuf,ms_u08 *outbuf, ms_s32 *plen )
{
	ms_bufcheckret(-1,pdrmval);
	ms_bufcheckret(-1,inbuf);
	ms_bufcheckret(-1,outbuf);
	if (ms_false==msdrm_innerapi_ishead(inbuf, (*plen) ) ){
		ms_errret(-3,"Not msdrm header");
	}
	MSDRMContext * pmsdrm_ctt=(MSDRMContext * )pdrmval;
	ms_u32 payloadlen=0;
	ms_u32 datalen=0;
	pmsdrm_ctt->opt=inbuf[msdrm_hopt_offset];
	msdrm_len_data(datalen, inbuf);	
	msdrm_len_payload(payloadlen, inbuf);

	//ms_debug("pmsdrm_ctt->opt:%d", pmsdrm_ctt->opt);
	msdrm_innerapi_endec(CRYPT_TYPE_DECRYPT, pmsdrm_ctt, &inbuf[msdrm_data_offset], outbuf, datalen);
	
	(*plen)=payloadlen;
	return  0;
}
static void msdrm_api_deinit(void *pdrmval)
{
	MSDRMContext * pmsdrm_ctt=(MSDRMContext * )pdrmval;
	if(ms_null!=pmsdrm_ctt){
		ms_freep(pmsdrm_ctt);
	}
}
//it is the export of this module
DODrm ffurl_drm_func={
	.name		=msstring_drmaes,
	.longname	="do drm data",
	.init			=msdrm_api_init,
	.setopt		=msdrm_api_setopt,
	.setkey		=msdrm_api_setkey,
	.enc   		=msdrm_api_enc,
	.dec   		=msdrm_api_dec,
	.deinit		=msdrm_api_deinit,
};
int msdrm_api_register(DODrm *table, int size)
{
	DODrm **p;
	if (size < sizeof(DODrm)) {
		DODrm* temp = ms_malloc(sizeof(DODrm),"DODrm");
		memcpy(temp, table, size);
		table = temp;
	}
	p = &first_drm;
	while (*p != ms_null){
		p = (DODrm **)&(*p)->next;
	}
	*p = table;
	table->next = ms_null;
	return 0;
}
void * msdrm_api_match(const char *name)
{
	DODrm **p= &first_drm;
	if(ms_null==name){
		ms_error( "Param error");
		return ms_null ;
	}
	while (*p != ms_null){
		if( ms_strncmps_aeq(name, (*p)->name)){
			break;
		}
		p =(DODrm **) &(*p)->next;
	}
	return (void *)*p ;
}	



//CLIENT SDK API
static MSDRMContext *pdrm_valgl=ms_null;
ms_void msdrm_oneapi_init(ms_u08 * ms_in key,ms_s32 ms_in len)
{
	pdrm_valgl=ms_null;
	pdrm_valgl=ffurl_drm_func.init();
	ffurl_drm_func.setkey(pdrm_valgl,key, len);
}
ms_s32 msdrm_oneapi_dec(ms_u08 *inbuf,ms_u08 *outbuf, ms_s32 *plen )
{
	return ffurl_drm_func.dec(pdrm_valgl,inbuf,outbuf, plen );
}
ms_void msdrm_oneapi_deinit(ms_void)
{
	ffurl_drm_func.deinit(pdrm_valgl);
}

//SERVER SDK API
typedef struct{
	ms_bool flag_stop;
	URLVal urlval_in;
	URLVal urlval_out;
	MSTHREADContext msthread_ctt;
}MSDEMAPPContext;
static MSDEMAPPContext msdrmadd_ctt;
static void * msdrm_decout(void *astrgv)
{
	MSDEMAPPContext  *pmsdrmadd_ctt=(MSDEMAPPContext  *)astrgv;
	URLVal *purlval_in=&pmsdrmadd_ctt->urlval_in;
	URLVal *purlval_out=&pmsdrmadd_ctt->urlval_out;
	ms_byte tbuf[2000]={0};
	ms_byte bufout[2000]={0};
	ms_s32 sendlen=0;
	ms_s32 recvlen=0;
	ms_s32 maxrecvlen=2000;
	msthread_api_setcurname(pmsdrmadd_ctt->msthread_ctt.name);

	if(msptc_http_vod==purlval_in->ptl_index
		||msptc_file==purlval_in->ptl_index){
		maxrecvlen=1344;
	}
	
	while(1){
		if(ms_true ==pmsdrmadd_ctt->flag_stop){
			return ms_null;
		}
		recvlen=purlval_in->purl_ptl->url_read(&purlval_in->url_ctt,tbuf,2000);
		if(recvlen>0){
			msdrm_oneapi_dec(tbuf, bufout,&recvlen);
			sendlen=purlval_out->purl_ptl->url_write(&purlval_out->url_ctt,bufout,recvlen);
			ms_usleep(10);
		}else{
			ms_usleep(50);
		}
	}
}
ms_void msdrm_oneapi_ptldeinit(ms_void)
{
	MSDEMAPPContext  *pmsdrmadd_ctt=&msdrmadd_ctt;

	pmsdrmadd_ctt->flag_stop=ms_true;
	ms_u64 sys_base=mstime_api_sec();
	while(ms_true==pmsdrmadd_ctt->flag_stop){
		ms_debug("Wait for msdrm_decout stop");
		ms_msleep(100);
		if(ms_true==mstime_api_counter_async((time_t *)&sys_base, ms_seconds(30))){
			break;
		}
	}
	msdrm_oneapi_deinit();
	msthread_api_destroy(&pmsdrmadd_ctt->msthread_ctt);
	
	URLVal *purlval_in=&pmsdrmadd_ctt->urlval_in;
	URLVal *purlval_out=&pmsdrmadd_ctt->urlval_out;
	if(ms_null!=purlval_in->purl_ptl){
		purlval_in->purl_ptl->url_close(&purlval_in->url_ctt);
	}
	if(ms_null!=purlval_out->purl_ptl){
		purlval_out->purl_ptl->url_close(&purlval_out->url_ctt);
	}
}
ms_s08 msdrm_oneapi_ptlinit(ms_u08 * ms_in urlin,ms_u08 * ms_in urlout,ms_u08 * ms_in key,ms_s32 ms_in len)
{
	mslog_api_setopt(53,ms_null,0);
	ms_bufcheckret_des(-1, urlin,"urlin");
	ms_bufcheckret_des(-1, urlout,"urlout");
	ms_bufcheckret_des(-1, key,"key");
	if ((len != 32)&& (len != 16) && (len != 24)){
		ms_errret(-1, "Keylen(%d) must be 32,16 or 24", len);
	}
	register_protocol();
	MSDEMAPPContext  *pmsdrmadd_ctt=&msdrmadd_ctt;
	ms_memset0(pmsdrmadd_ctt, sizeof(MSDEMAPPContext));
	
	msdrm_oneapi_init(key, len);
	
	URLVal *purlval_in=&pmsdrmadd_ctt->urlval_in;
	ms_memset0(purlval_in, sizeof(URLVal));
	ms_strcpy(purlval_in->url_ctt.url, urlin);
	purlval_in->purl_ptl=msprotocol_api_matchurl2(purlval_in->url_ctt.url,&purlval_in->url_ctt.url_info);
	purlval_in->ptl_index=msprotocol_api_getprotocol(urlin, ms_null, ms_false);
	
	if(ms_null==purlval_in->purl_ptl){
		ms_errgoto(ms_failed, "Unfind  protocol in");
	}
	if(purlval_in->purl_ptl->url_open(&purlval_in->url_ctt)<0){
		ms_errgoto(ms_failed, "Open  %s failed",purlval_in->url_ctt.url);
	}	

	URLVal *purlval_out=&pmsdrmadd_ctt->urlval_out;
	ms_memset0(purlval_out, sizeof(URLVal));
	ms_strcpy(purlval_out->url_ctt.url,urlout);
	purlval_out->purl_ptl=msprotocol_api_matchurl2(purlval_out->url_ctt.url,&purlval_out->url_ctt.url_info);
	purlval_out->ptl_index=msprotocol_api_getprotocol(urlout, ms_null, ms_false);
	if(ms_null==purlval_out->purl_ptl){
		ms_errgoto(ms_failed,  "Unfind  protocol in");
	}
	purlval_out->url_ctt.flags=FLAG_WRITE;
	if(purlval_out->purl_ptl->url_open(&purlval_out->url_ctt)<0){
		ms_errgoto(ms_failed,  "Open  %s failed",purlval_out->url_ctt.url);
	}	
	msthread_api_create(&pmsdrmadd_ctt->msthread_ctt, "msdrm_decout", msdrm_decout, pmsdrmadd_ctt);
	return 0;
	
ms_failed:
	msdrm_oneapi_ptldeinit();
	return -1;
}
ms_void msdrm_api_infofunc(ms_string ms_out pbuf)
{
#if defined OS_LINUX_SOC  
	strcat(pbuf, "linux_soc");
#else
	strcat(pbuf, "unlinux_soc");
#endif
	strcat(pbuf, "|crc");
	strcat(pbuf, "|des");
	strcat(pbuf, "|aes");
}
ms_string msdrm_api_version(ms_void)
{
	return "01.00.05";
}
#undef MSDRM_C

